package com.mali.user.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.PhoneUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpStatus;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mali.api.client.PermissionClient;
import com.mali.common.annotation.OperationLog;
import com.mali.common.db.enums.RoleEnum;
import com.mali.common.db.util.RedisUtil;
import com.mali.common.dto.*;
import com.mali.common.util.PasswordEncoderUtil;
import com.mali.common.util.UserContext;
import com.mali.common.web.domain.dto.*;
import com.mali.mq.template.LogTemplate;
import com.mali.user.config.JwtProperties;
import com.mali.user.domain.vo.AllUserVO;
import com.mali.user.domain.vo.LoginVO;
import com.mali.user.domain.vo.RegisterVO;
import com.mali.user.domain.vo.UserVO;
import com.mali.user.mapper.UserMapper;
import com.mali.user.domain.po.User;
import com.mali.user.service.UserService;
import com.mali.user.util.JwtUtil;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;

import static com.mali.common.util.RedisConstant.*;
import static com.mali.common.util.RoleConstant.*;

@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final RedisUtil redisUtil;
    private final PermissionClient permissionClient;
    private final LogTemplate logTemplate;
    private final JwtUtil jwtUtil;
    private final JwtProperties jwtProperties;
    @Value("${spring.application.name}")
    private String serviceName;

    @Override
    @OperationLog(module = "GET_CODE_BY_PHONE", operation = "GET_CODE_BY_PHONE", serviceName = "user-service")
    public Result<String> getCode(CodeDTO codeDTO) {
        String phone = codeDTO.getPhone();
        if (StrUtil.isEmpty(phone)) {
            return Result.fail("手机号为空");
        }

        if (!PhoneUtil.isMobile(phone)) {
            return Result.fail("手机号不合法");
        }

        // 生成随机验证码
        String code = RandomUtil.randomString(6);
        final String codeToken = REDIS_KEY_AUTH_CODE + phone;

        // 保存验证码到 Redis
        redisUtil.set(codeToken, code);
        redisUtil.expire(codeToken, 60 * 5);

        return Result.success(code);
    }

    @Override
    @GlobalTransactional(rollbackFor = Exception.class, name = "user-register-tx")
    @OperationLog(module = "REGISTER_BY_CODE", operation = "REGISTER_BY_CODE", serviceName = "user-service")
    public Result<RegisterVO> register(RegisterDTO registerDTO) {
        String code = registerDTO.getCode();
        String phone = registerDTO.getPhone();

        if (StrUtil.isEmpty(code) || StrUtil.isEmpty(phone)) {
            return Result.fail("手机号或验证码为空");
        }

        String cacheCode = redisUtil.get(REDIS_KEY_AUTH_CODE + phone);
        if (StrUtil.isEmpty(cacheCode)) {
            return Result.fail("未获取验证码或验证码已过期");
        }

        if (!code.equals(cacheCode)) {
            return Result.fail("验证码错误");
        }

        redisUtil.delete(REDIS_KEY_AUTH_CODE + phone);

        // 创建用户
        User user = new User();
        String rawPassword = RandomUtil.randomString(10);
        String password = PasswordEncoderUtil.encode(rawPassword);
        user.setPhone(phone);
        user.setPassword(password);
        user.setGmt_create(new Date(System.currentTimeMillis()));

        boolean saveSuccess = save(user);
        if (!saveSuccess) {
            return Result.fail("注册失败，请稍后重试");
        }

        // RPC 调用绑定默认角色
        Result<String> bindResult = permissionClient.defaultRole(user.getId());
        if (!bindResult.success()) {
            throw new RuntimeException("绑定默认角色失败：" + bindResult.getMessage());
        }

        RegisterVO registerVO = new RegisterVO();
        registerVO.setRole(bindResult.getData());
        registerVO.setPassword(rawPassword);

        return Result.success(registerVO);
    }

    @Override
    @OperationLog(module = "LOGIN_WITH_PASSWORD", operation = "LOGIN_WITH_PASSWORD", serviceName = "user-service")
    public Result<LoginVO> login(PasswordLoginDTO passwordLoginDTO) {
        String phone = passwordLoginDTO.getPhone();
        String password = passwordLoginDTO.getPassword();

        if (!PhoneUtil.isPhone(phone)) {
            return Result.fail("手机号格式错误");
        }

        if (StrUtil.isEmpty(password)) {
            return Result.fail("密码为空");
        }

        User user = lambdaQuery().eq(User::getPhone, phone).one();
        if (user == null) {
            return Result.fail("手机号不存在");
        }

        String userPassword = user.getPassword();
        boolean matches = PasswordEncoderUtil.matches(password, userPassword);
        if (!matches) {
            return Result.fail("手机号或密码错误");
        }

        return getLoginVOResult(user);
    }

    @Override
    @OperationLog(module = "LOGIN_WITH_CODE", operation = "LOGIN_WITH_CODE", serviceName = "user-service")
    public Result<LoginVO> login(CodeLoginDTO codeLoginDTO) {
        String code = codeLoginDTO.getCode();
        String phone = codeLoginDTO.getPhone();

        if (!PhoneUtil.isPhone(phone)) {
            return Result.fail("手机号格式错误");
        }

        if (StrUtil.isEmpty(code)) {
            return Result.fail("验证码为空");
        }

        String cacheCode = redisUtil.get(REDIS_KEY_AUTH_CODE + phone);
        if (StrUtil.isEmpty(cacheCode)) {
            return Result.fail("不存在该手机号的验证码");
        }

        if (!code.equals(cacheCode)) {
            return Result.fail("验证码错误");
        }

        // 验证码正确，删除验证码
        redisUtil.delete(REDIS_KEY_AUTH_CODE + phone);

        User user = lambdaQuery().eq(User::getPhone, phone).one();
        if (user == null) {
            return Result.fail("该用户不存在");
        }

        return getLoginVOResult(user);
    }



    @Override
    @OperationLog(module = "GET_USER_LIST", operation = "GET_USER_LIST", serviceName = "user-service")
    public Result<AllUserVO> getUserList(PageRequestDTO pageRequestDTO) {
        Long userId = UserContext.getUserId();

        // 参数校验
        if (userId == null) {
            return Result.fail("用户ID为空");
        }
        if (pageRequestDTO == null) {
            return Result.fail("分页参数为空");
        }

        // 尝试从缓存中获取角色权限码
        Integer roleCode;
        String userRoleKey = REDIS_KEY_USER_ROLE + userId;
        String userRoleStr = redisUtil.get(userRoleKey);
        roleCode = Convert.toInt(userRoleStr, null);

        if(!Objects.nonNull(roleCode)){
            // 调用权限服务获取角色码
            Result<Integer> roleResult = permissionClient.getCode(userId);
            if (roleResult.getCode() != 200 || roleResult.getData() == null) {
                return Result.fail("权限码获取失败");
            }
            roleCode = roleResult.getData();
        }

        AllUserVO allUserVO = new AllUserVO();
        Set<Long> commonUserSet = new HashSet<>();
        Set<Long> adminUserSet = new HashSet<>();

        try {
            switch (roleCode) {
                case ROLE_USER_CODE -> {
                    commonUserSet.add(userId);
                }
                case ROLE_ADMIN_CODE -> {
                    Result<Collection<Long>> commonUserResult = permissionClient.getCommonUserList(pageRequestDTO);
                    if (commonUserResult.getCode() != 200) {
                        return Result.fail("获取普通用户列表失败");
                    }
                    commonUserSet.addAll(commonUserResult.getData());
                }
                case ROLE_SUPER_ADMIN_CODE -> {
                    Result<Collection<Long>> commonUserResult = permissionClient.getCommonUserList(pageRequestDTO);
                    if (commonUserResult.getCode() != 200) {
                        return Result.fail("获取普通用户列表失败");
                    }
                    commonUserSet.addAll(commonUserResult.getData());

                    Result<Collection<Long>> adminUserResult = permissionClient.getAdminUserList(pageRequestDTO);
                    if (adminUserResult.getCode() != 200) {
                        return Result.fail("获取管理员列表失败");
                    }
                    adminUserSet.addAll(adminUserResult.getData());
                }
                default -> {
                    return Result.fail("权限码错误");
                }
            }
        } catch (Exception e) {
            return Result.fail("获取用户列表失败：" + e.getMessage());
        }

        // 返回数据
        allUserVO.setCommonUserList(commonUserSet);
        allUserVO.setAdminUserList(adminUserSet);

        return Result.success(allUserVO);
    }

    @Override
    @OperationLog(module = "GET_USER_INFO", operation = "GET_USER_INFO", serviceName = "user-service")
    public Result<UserVO> getUserInfo(Long userId) {
        Long operatorId = UserContext.getUserId();

        if (userId == null || operatorId == null) {
            return Result.fail("参数错误");
        }

        Integer operatorRoleCode = getRoleCode(operatorId);
        if (operatorRoleCode == null) {
            return Result.fail("无法获取权限信息");
        }

        try {
            if (operatorRoleCode.equals(ROLE_SUPER_ADMIN_CODE)) {
                return getUserWithResolvedRole(userId);
            }

            if (operatorRoleCode.equals(ROLE_USER_CODE)) {
                if (!userId.equals(operatorId)) {
                    return Result.fail("权限不足");
                }
                return getUserWithRole(userId, RoleEnum.USER);
            }

            if (operatorRoleCode.equals(ROLE_ADMIN_CODE)) {
                Integer targetRoleCode = getRoleCode(userId);
                if (targetRoleCode == null) {
                    return Result.fail("查询目标用户权限失败");
                }

                if (!targetRoleCode.equals(ROLE_USER_CODE)) {
                    return Result.fail("权限不足");
                }
                return getUserWithRole(userId, RoleEnum.ADMIN);
            }
        } catch (Exception e) {
            return Result.fail("系统异常：" + e.getMessage());
        }

        return Result.fail("无效权限码");
    }

    @Override
    @GlobalTransactional(rollbackFor = Exception.class, name = "user-update-tx")
    @OperationLog(module = "UPDATE_USER", operation = "UPDATE_USER", serviceName = "user-service")
    public Result<String> updateUser(Long userId, UserUpdateDTO updateDTO) {
        Long operatorId = UserContext.getUserId();

        // 参数校验
        if (userId == null || operatorId == null || updateDTO == null) {
            return Result.fail("参数错误");
        }

        // 获取权限信息
        Integer operatorCode = permissionClient.getCode(operatorId).getData();
        Integer targetUserCode = permissionClient.getCode(userId).getData();
        if (operatorCode == null || targetUserCode == null) {
            return Result.fail("权限信息异常");
        }

        Integer newRole = updateDTO.getRole(); // 可能为 null

        // 权限校验
        if (operatorCode.equals(ROLE_USER_CODE)) {
            if (!operatorId.equals(userId)) {
                return Result.fail("权限不足");
            }
            if (newRole != null && !newRole.equals(RoleEnum.USER.getCode())) {
                return Result.fail("普通用户不能修改权限");
            }
        }

        if (operatorCode.equals(ROLE_ADMIN_CODE)) {
            if (!operatorId.equals(userId) && !targetUserCode.equals(ROLE_USER_CODE)) {
                return Result.fail("权限不足");
            }
            if (!operatorId.equals(userId) && newRole != null && !newRole.equals(RoleEnum.USER.getCode())) {
                return Result.fail("管理员不能修改权限等级");
            }
        }

        if (operatorCode.equals(ROLE_SUPER_ADMIN_CODE) && operatorId.equals(userId)) {
            if (newRole != null && !newRole.equals(RoleEnum.SUPER_ADMIN.getCode())) {
                return Result.fail("超级管理员不能修改自己的权限");
            }
        }

        // 更新用户信息
        User user = new User();
        updateDtoToPO(updateDTO, user);
        user.setId(userId);

        boolean updateRes = lambdaUpdate()
                .eq(User::getId, userId)
                .update(user);

        if (!updateRes) {
            return Result.fail("修改失败");
        }

        // 更新权限（仅超级管理员可操作）
        if (operatorCode.equals(ROLE_SUPER_ADMIN_CODE) && newRole != null) {
            if (newRole.equals(RoleEnum.ADMIN.getCode())) {
                permissionClient.updateToAdmin(userId);
            } else if (newRole.equals(RoleEnum.USER.getCode())) {
                permissionClient.updateToUser(userId);
            }
        }

        return Result.success("修改成功");
    }

    @Override
    @GlobalTransactional(rollbackFor = Exception.class, name = "user-reset-pwd-tx")
    @OperationLog(module = "RESET_PASSWORD", operation = "RESET_PASSWORD", serviceName = "user-service")
    public Result<String> resetPassword(@NotNull ResetPwdDTO resetPwdDTO) {
        Long userId = resetPwdDTO.getUserId();
        Long operatorId = UserContext.getUserId();
        String newPassword = resetPwdDTO.getNewPassword();

        // 参数校验
        if (userId == null || operatorId == null || StrUtil.isBlank(newPassword)) {
            return Result.fail("参数不能为空");
        }

        // 获取操作人权限
        Result<Integer> operatorRoleRes = permissionClient.getCode(operatorId);
        if (!operatorRoleRes.getCode().equals(HttpStatus.HTTP_OK) || operatorRoleRes.getData() == null) {
            return Result.fail("获取操作人权限失败");
        }
        int operatorRoleCode = operatorRoleRes.getData();

        // 获取目标用户权限
        Result<Integer> targetRoleRes = permissionClient.getCode(userId);
        if (targetRoleRes.getCode() != HttpStatus.HTTP_OK || targetRoleRes.getData() == null) {
            return Result.fail("获取目标用户权限失败");
        }
        int targetRoleCode = targetRoleRes.getData();

        // 权限校验
        if (operatorRoleCode == ROLE_USER_CODE && !operatorId.equals(userId)) {
            return Result.fail("权限不足，不能重置其他用户密码");
        }
        if (operatorRoleCode == ROLE_ADMIN_CODE && targetRoleCode != ROLE_USER_CODE) {
            return Result.fail("权限不足，只能重置普通用户密码");
        }

        // 验证用户存在性
        User user = lambdaQuery().eq(User::getId, userId).one();
        if (user == null) {
            return Result.fail("目标用户不存在");
        }

        // 更新密码
        String encodedPassword = PasswordEncoderUtil.encode(newPassword);
        boolean updated = lambdaUpdate()
                .eq(User::getId, userId)
                .set(User::getPassword, encodedPassword)
                .update();

        if (!updated) {
            return Result.fail("密码重置失败");
        }

        return Result.success("密码重置成功");
    }

    private void userPoToVo(User user, UserVO userVO) {
        userVO.setId(user.getId());
        userVO.setPhone(user.getPhone());
        userVO.setEmail(user.getEmail());
        userVO.setGmt_create(user.getGmt_create());
        userVO.setUsername(user.getUsername());
    }

    private void userDtoToPo(UserDTO userDto, User user) {
        user.setUsername(userDto.getUsername());
        user.setPhone(userDto.getPhone());
        user.setEmail(userDto.getEmail());
    }

    private void updateDtoToPO(UserUpdateDTO updateDTO, User user) {
        user.setUsername(updateDTO.getUsername());
        user.setPhone(updateDTO.getPhone());
        user.setEmail(updateDTO.getEmail());
    }

    private Result<UserVO> getUserWithRole(Long userId, RoleEnum role) {
        log.info("[用户模块]-[获取{}信息] 获取用户信息，用户ID：{}", role.name(), userId);

        User user = lambdaQuery().eq(User::getId, userId).one();
        if (user == null) {
            log.warn("[用户模块]-[获取{}信息]-[失败] 用户不存在", role.name());
            logTemplate.sendFailLog(serviceName, "user", "GET_USER_INFO", null, userId, "用户不存在");
            return Result.fail("该用户不存在");
        }
        UserVO userVO = new UserVO();
        userPoToVo(user, userVO);
        userVO.setRole(role.name());
        log.info("[用户模块]-[获取{}信息]-[成功] 获取用户信息成功，用户信息: {}", role.name(), user);
        logTemplate.sendSuccessLog(serviceName, "user", "GET_USER_INFO", null, userId, "获取用户信息成功");

        return Result.success(userVO);
    }

    @Nullable
    private Integer getRoleCode(Long userId) {
        Result<Integer> result = permissionClient.getCode(userId);
        if (result == null || !Objects.equals(result.getCode(), HttpStatus.HTTP_OK)) {
            return null;
        }
        return result.getData();
    }

    /**
     * 超管专用，根据目标用户权限动态选择返回角色封装
     */
    private Result<UserVO> getUserWithResolvedRole(Long userId) {
        Integer roleCode = getRoleCode(userId);
        if (roleCode == null) {
            log.warn("[用户模块]-[超管]-[失败] 无法获取目标用户权限 userId={}", userId);
            logTemplate.sendFailLog(serviceName, "user", "GET_USER_INFO_SUPER_ADMIN", null, userId, "无法获取目标用户权限");
            return Result.fail("无法获取目标用户权限");
        }

        RoleEnum role;
        switch (roleCode) {
            case ROLE_USER_CODE -> role = RoleEnum.USER;
            case ROLE_ADMIN_CODE -> role = RoleEnum.ADMIN;
            case ROLE_SUPER_ADMIN_CODE -> role = RoleEnum.SUPER_ADMIN;
            default -> {
                log.warn("[用户模块]-[超管]-[失败] 未知的权限码：{}", roleCode);
                logTemplate.sendFailLog(serviceName, "user", "GET_USER_INFO_SUPER_ADMIN", null, userId, "未知权限码");
                return Result.fail("未知权限码");
            }
        }

        return getUserWithRole(userId, role);
    }

    @NotNull
    private Result<LoginVO> getLoginVOResult(User user) {
        Long userId = user.getId();
        String token = jwtUtil.createToken(userId, jwtProperties.getTokenTTL());

        LoginVO loginVO = new LoginVO();
        loginVO.setToken(token);
        loginVO.setUserId(userId);

        redisUtil.set(REDIS_KEY_USER_TOKEN + userId, token);
        redisUtil.expire(REDIS_KEY_USER_TOKEN + userId, 60 * 30);

        return Result.success(loginVO);
    }
}
